/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-05-21     chenhy       the first version
 */
#include "board.h"
#include "uart.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#ifdef USING_UART1
#define UART1_RTS_CLR()   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET)
#define UART1_RTS_SET()   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET)
unsigned char uart1_cache[UART_CACHE_SIZE];
UART_HandleTypeDef huart1;
#else
#define UART1_RTS_CLR()
#define UART1_RTS_SET()
#endif
#ifdef USING_UART2
unsigned char uart2_cache[UART_CACHE_SIZE];
UART_HandleTypeDef huart2;
#endif
#ifdef USING_UART3
unsigned char uart3_cache[UART_CACHE_SIZE];
UART_HandleTypeDef huart3;
#endif
#ifdef USING_SHELL
void rt_hw_console_input(unsigned char *pbuf, int size);
#endif
int uart_init(void)
{
#ifdef USING_UART1
  uart1_init();
#endif
#ifdef USING_UART2
  uart2_init();
#endif
#ifdef USING_UART3
  uart3_init();
#endif
  return 0;
}
INIT_BOARD_EXPORT(uart_init);

int uart_deinit(void)
{
#ifdef USING_UART1
  HAL_UART_DeInit(&huart1);
#endif
#ifdef USING_UART2
  HAL_UART_DeInit(&huart2);
#endif
#ifdef USING_UART3
  HAL_UART_DeInit(&huart3);
#endif
  return 0;
}

#ifdef USING_UART1
int uart1_init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  HAL_UART_DeInit(&huart1);
  HAL_UART_Init(&huart1);
  HAL_UART_DMAStop(&huart1);
  __HAL_UART_CLEAR_IDLEFLAG(&huart1);
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
  HAL_UART_Receive_DMA(&huart1, uart1_cache, UART_CACHE_SIZE);
  return 0;
}
#endif

#ifdef USING_UART2
int uart2_init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  HAL_UART_DeInit(&huart2);
  HAL_UART_Init(&huart2);
  HAL_UART_DMAStop(&huart2);
  __HAL_UART_CLEAR_IDLEFLAG(&huart2);
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
  HAL_UART_Receive_DMA(&huart2, uart2_cache, UART_CACHE_SIZE);
  return 0;
}
#endif

#ifdef USING_UART3
int uart3_init(void)
{
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  HAL_UART_DeInit(&huart3);
  HAL_UART_Init(&huart3);
  HAL_UART_DMAStop(&huart3);
  __HAL_UART_CLEAR_IDLEFLAG(&huart3);
  __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
  HAL_UART_Receive_DMA(&huart3, uart3_cache, UART_CACHE_SIZE);
  return 0;
}
#endif

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  int recv_size = 0;
  recv_size = UART_CACHE_SIZE-__HAL_DMA_GET_COUNTER(huart->hdmarx);
  if(recv_size) {
    uart_recv_callback(huart, huart->pRxBuffPtr, recv_size);
#ifdef USING_SHELL
    if(huart == &huart2)
      rt_hw_console_input(huart->pRxBuffPtr, recv_size);
#endif
    HAL_UART_AbortReceive(huart);
    HAL_UART_Receive_DMA(huart, huart->pRxBuffPtr, UART_CACHE_SIZE);
  }
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
#ifdef USING_UART1
  if(huart->Instance == USART1) {
    uart1_init();
  }
#endif
#ifdef USING_UART2
  if(huart->Instance == USART1) {
    uart2_init();
  }
#endif
#ifdef USING_UART3
  if(huart->Instance == USART1) {
    uart3_init();
  }
#endif
}

int uart_write(UART_HandleTypeDef *huart, void* pbuf, int size)
{
  if(pbuf == NULL) return 0;
  while(huart->gState != HAL_UART_STATE_READY);
  if(size == 0) return size;
  if(huart->Instance == USART1) UART1_RTS_SET();
  HAL_UART_Transmit(huart, (unsigned char*)pbuf, size, HAL_MAX_DELAY);
  if(huart->Instance == USART1) UART1_RTS_CLR();
  return size;
}

__weak void uart_recv_callback(UART_HandleTypeDef *huart, unsigned char* pbuf, int size)
{
  uart_write(huart, pbuf, size);
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
  GPIO_InitTypeDef gpio_init;
#ifdef USING_UART1
  if(huart->Instance == USART1) {
    static DMA_HandleTypeDef hdma_rx1;
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    gpio_init.Pin = GPIO_PIN_6|GPIO_PIN_7;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_PULLUP;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init.Alternate = GPIO_AF0_USART1;
    HAL_GPIO_Init(GPIOB, &gpio_init);

    HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);

    hdma_rx1.Instance = DMA1_Channel3;
    hdma_rx1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_rx1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_rx1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_rx1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_rx1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_rx1.Init.Mode = DMA_NORMAL;
    hdma_rx1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_Init(&hdma_rx1);
    __HAL_DMA1_REMAP(HAL_DMA1_CH3_USART1_RX);
    __HAL_LINKDMA(huart, hdmarx, hdma_rx1);

    HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
  }
#endif
#ifdef USING_UART2
  if(huart->Instance == USART2) {
    static DMA_HandleTypeDef hdma_rx2;
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART2_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    gpio_init.Pin = GPIO_PIN_2|GPIO_PIN_3;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_PULLUP;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init.Alternate = GPIO_AF1_USART2;
    HAL_GPIO_Init(GPIOA, &gpio_init);

    HAL_NVIC_SetPriority(USART2_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);

    hdma_rx2.Instance = DMA1_Channel5;
    hdma_rx2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_rx2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_rx2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_rx2.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_rx2.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_rx2.Init.Mode = DMA_NORMAL;
    hdma_rx2.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_Init(&hdma_rx2);
    __HAL_DMA1_REMAP(HAL_DMA1_CH5_USART2_RX);
    __HAL_LINKDMA(huart, hdmarx, hdma_rx2);

    HAL_NVIC_SetPriority(DMA1_Channel4_5_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
  }
#endif
#ifdef USING_UART3
  if(huart->Instance == USART3) {
    static DMA_HandleTypeDef hdma_rx3;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_USART3_CLK_ENABLE();
    __HAL_RCC_DMA1_CLK_ENABLE();

    gpio_init.Pin = GPIO_PIN_10|GPIO_PIN_11;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_PULLUP;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init.Alternate = GPIO_AF4_USART3;
    HAL_GPIO_Init(GPIOB, &gpio_init);

    HAL_NVIC_SetPriority(USART3_6_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(USART3_6_IRQn);

    hdma_rx3.Instance = DMA1_Channel1;
    hdma_rx3.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_rx3.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_rx3.Init.MemInc = DMA_MINC_ENABLE;
    hdma_rx3.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_rx3.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_rx3.Init.Mode = DMA_NORMAL;
    hdma_rx3.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    HAL_DMA_Init(&hdma_rx3);
    __HAL_DMA1_REMAP(HAL_DMA1_CH1_USART3_RX);
    __HAL_LINKDMA(huart, hdmarx, hdma_rx3);

    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  }
#endif
}

void HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
{
#ifdef USING_UART1
  if(huart->Instance == USART1) {
    __HAL_RCC_USART1_FORCE_RESET();
    __HAL_RCC_USART1_RELEASE_RESET();
    __HAL_RCC_USART1_CLK_DISABLE();
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7);
    HAL_NVIC_DisableIRQ(USART1_IRQn);
    if(huart->hdmarx != 0) HAL_DMA_DeInit(huart->hdmarx);
    if(huart->hdmatx != 0) HAL_DMA_DeInit(huart->hdmatx);
    HAL_NVIC_DisableIRQ(DMA1_Channel2_3_IRQn);
  }
#endif
#ifdef USING_UART2
  if(huart->Instance == USART2) {
    __HAL_RCC_USART2_FORCE_RESET();
    __HAL_RCC_USART2_RELEASE_RESET();
    __HAL_RCC_USART2_CLK_DISABLE();
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2);
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_3);
    HAL_NVIC_DisableIRQ(USART2_IRQn);
    if(huart->hdmarx != 0) HAL_DMA_DeInit(huart->hdmarx);
    if(huart->hdmatx != 0) HAL_DMA_DeInit(huart->hdmatx);
    HAL_NVIC_DisableIRQ(DMA1_Channel4_5_IRQn);
  }
#endif
#ifdef USING_UART3
  if(huart->Instance == USART3) {
    __HAL_RCC_USART3_FORCE_RESET();
    __HAL_RCC_USART3_RELEASE_RESET();
    __HAL_RCC_USART3_CLK_DISABLE();
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10);
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11);
    HAL_NVIC_DisableIRQ(USART3_6_IRQn);
    if(huart->hdmarx != 0) HAL_DMA_DeInit(huart->hdmarx);
    if(huart->hdmatx != 0) HAL_DMA_DeInit(huart->hdmatx);
    HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);
  }
#endif
}

int _write(int file, char *data, int len)
{
#if 1
  int i;
  for(i=0;i<len;i++) {
    if(data[i] == '\n')
      HAL_UART_Transmit(&huart2, (unsigned char*)"\r", 1, HAL_MAX_DELAY);
    HAL_UART_Transmit(&huart2, (unsigned char*)&data[i], 1, HAL_MAX_DELAY);
  }
#endif
  return len;
}

void print_buffer(unsigned char *data, int length)
{
  int i;
  for( i = 0; i < length; i++ ) {
    printf("%02X ", data[i]);
  }
  printf("\n");
}

#ifdef USING_SHELL
void rt_hw_console_output(const char *str)
{
  int len, i;
  len = strlen(str);
  for(i=0;i<len;i++) {
    if(str[i] == '\n')
      HAL_UART_Transmit(&huart2, (unsigned char*)"\r", 1, HAL_MAX_DELAY);
    HAL_UART_Transmit(&huart2, (unsigned char*)&str[i], 1, HAL_MAX_DELAY);
  }
}

/**
 * This function will put string to the console.
 *
 * @param str the string output to the console.
 */
void rt_kputs(const char *str)
{
  if (!str) return;
  rt_hw_console_output(str);
}

/**
 * This function will print a formatted string on system console
 *
 * @param fmt the format
 */
void rt_kprintf(const char *fmt, ...)
{
  va_list args;
  rt_size_t length;
  static char rt_log_buf[RT_CONSOLEBUF_SIZE];

  va_start(args, fmt);
  /* the return value of vsnprintf is the number of bytes that would be
    * written to buffer had if the size of the buffer been sufficiently
    * large excluding the terminating null byte. If the output string
    * would be larger than the rt_log_buf, we have to adjust the output
    * length. */
  length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
  if (length > RT_CONSOLEBUF_SIZE - 1)
    length = RT_CONSOLEBUF_SIZE - 1;
  rt_hw_console_output(rt_log_buf);
  va_end(args);
}

#include "shell.h"
static char console_cache[32];
static int cache_item_cnt = 0;
extern struct finsh_shell *shell;
void rt_hw_console_input(unsigned char *pbuf, int size)
{
  int i;
  if(size > (sizeof(console_cache) - cache_item_cnt))
    size = sizeof(console_cache) - cache_item_cnt;
  if(!cache_item_cnt) 
    rt_sem_release(&shell->rx_sem);
  for(i=0;i<size;i++) {
    console_cache[cache_item_cnt] = pbuf[i];
    cache_item_cnt ++;
  }
}

char rt_hw_console_getchar(void)
{
  char item;
  if(cache_item_cnt == 0) 
    rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
  item = console_cache[0];
  cache_item_cnt --;
  memmove(&console_cache[0], &console_cache[1], cache_item_cnt);
  return item;
}
#endif
